//file: MemAllocLog.h
#ifndef MEMALLOCLOG_H
#define MEMALLOCLOG_H

#include <stddef.h>
#include <time.h>

#define _FUNC_MALLOC     'a'
#define _FUNC_REALLOC    'r'
#define _FUNC_CALLOC     'c'
#define _FUNC_FREE       'f'

struct __MemAllocLog{
    /**
     * func:
     * a:malloc
     * r:realloc
     * f:free
     */
    char func;

    /*cnt个元素, 每个元素大小memSize*/
    size_t cnt;
    size_t memSize;
    void* memAddr;
    void* memAddrOld;
    struct timespec timespecCurrent;
    void* callerAddr;
    unsigned long threadId;
};
typedef struct __MemAllocLog MAItem;

#define __MemAllocLog_TYPE_BYTE_SIZE  (sizeof(MALog))


#define MEM_ALLOC_LOG_LIMIT (1024*20)

//MemAlloc调用次数计数器
struct __MemAllocCounter{
    unsigned int mallocCnt;
    unsigned int reallocCnt;
    unsigned int freeCnt;
};
typedef struct __MemAllocCounter MACounter;

struct __MemAllocRoot{
//{MemAlloc调用日志数组 开始
    //日志数组
    MAItem logArr[MEM_ALLOC_LOG_LIMIT];
    //日志数组中 业务数据范围为 [0,curIdx]
    unsigned int curIdx;
//MemAlloc调用日志数组 结束}

//{MemAlloc调用次数计数器 开始
    MACounter counter;
//MemAlloc调用次数计数器 结束}
};
typedef struct __MemAllocRoot MARoot;


MARoot gMARoot;


void gMA_init( ){
    gMARoot.curIdx=0;
    gMARoot.counter.mallocCnt=0;
    gMARoot.counter.reallocCnt=0;
    gMARoot.counter.freeCnt=0;
}

//数组未满吗？
int gMA_notFull(void){
    int ok=(gMARoot.curIdx >= 0 && gMARoot.curIdx < MEM_ALLOC_LOG_LIMIT );
    return ok;
}

void gMA_doCount(char func){
    switch(func){
        case _FUNC_MALLOC: { //'m':
            gMARoot.counter.mallocCnt++;
            break;
        }
        case _FUNC_REALLOC:{//'r':
            gMARoot.counter.reallocCnt++;
            break;
        }
        case _FUNC_FREE:{//'f':
            gMARoot.counter.freeCnt++;
            break;
        }
        default:{
            break;
        }
    }
    return  ;
}

void gMA_addItem(char func, size_t cnt, size_t memSize, void* memAddr, void* memAddrOld, void* callerAddr,unsigned long threadId){
    //日志数目计数
    gMA_doCount(func);

    //若日志数组已满, 则拒绝添加当前日志，直接返回
    if(!gMA_notFull()){
        return;
    }
    //走到这里, 肯定是日志数组不满，可以添加当前日志

    //{向日志数组中添加一条日志 开始
    MAItem* logPtr= gMARoot.logArr + gMARoot.curIdx;

    //取当前 精准的时刻：毫秒数、纳秒数
    clock_gettime(CLOCK_REALTIME, &(logPtr->timespecCurrent));

    //字段赋值
    logPtr->func=func;
    logPtr->cnt=cnt;
    logPtr->memSize=memSize;
    logPtr->memAddr=memAddr;
    logPtr->memAddrOld=memAddrOld;
    logPtr->callerAddr=callerAddr;
    logPtr->threadId=threadId;

    //日志数组当前下标向后移动
    gMARoot.curIdx++;

    //{向日志数组中添加一条日志 结束

    return;
}


void gMA_log(char func, size_t elemCnt, size_t memSize, void* memAddr, void* memAddrOld, void* callerAddr,unsigned long threadId){
    if(func==_FUNC_FREE){
        gMA_addItem(func, 0, 0, memAddr, memAddrOld, callerAddr,threadId);
    }else if(func==_FUNC_MALLOC || func==_FUNC_REALLOC ){
        gMA_addItem(func, 1, memSize, memAddr, memAddrOld, callerAddr,threadId);
    }else if(func==_FUNC_CALLOC ){
        gMA_addItem(func, elemCnt, memSize, memAddr, memAddrOld, callerAddr,threadId);
    }
}

#define HumanDateTimeFormat "%d%02d%02d_%02d%02d%02d"
//HmDateTimeSz: HumanDateTimeStrSize
#define  HmDateTimeSz (strlen(HumanDateTimeFormat)+1)
/**
 * 转为 人类可读当前日期时刻
 * toHumanDateTimeStr: toHmDateTimeStr
 * @param p_datetimeCurrent
 * @param humanDateTimeStr
 */
void toHmDateTimeStr(struct tm * p_datetimeCurrent, char * humanDateTimeStr) {
    //人类可读当前日期时刻
//    char humanDateTimeStr[HmDateTimeSz];
    sprintf(humanDateTimeStr,HumanDateTimeFormat, p_datetimeCurrent->tm_year + 1900, p_datetimeCurrent->tm_mon + 1, p_datetimeCurrent->tm_mday, p_datetimeCurrent->tm_hour, p_datetimeCurrent->tm_min, p_datetimeCurrent->tm_sec);
}

void genFileName(char* outFileName, char* title, char* processName,struct tm * p_datetimeCurrent){

    //人类可读当前日期时刻
    char hmDateTimeStr[HmDateTimeSz];
    toHmDateTimeStr(p_datetimeCurrent, hmDateTimeStr);

    sprintf(outFileName,"./_%s_%s_%s.out",title, processName,hmDateTimeStr);
}
#define  PROC_NAME_LIMIT 128

/** 保存全局变量中的日志
 *
 * @param processName 进程名 ，信任该进程名不会太长
 * @param datetimeCurrent 粗略的时刻: 当前日期时刻
 * @return
 */
void gMA_save(char* processName,struct tm * pNow){

    //人类可读当前日期时刻
//    char humanDateTimeStr[HmDateTimeSz];
//    toHmDateTimeStr(pNow, humanDateTimeStr);

    //由于本函数g_memAlloc_save内的  fprintf  会调用malloc, 而malloc调用会导致g_memAlloc.curIdx自增, 因此有必要使用一个不变量 memAllocCurIdx 暂存g_memAlloc.curIdx
    int memAllocCurIdx = gMARoot.curIdx;

//   struct timespec timespecCurrent;
//   clock_gettime(CLOCK_REALTIME, &(timespecCurrent));


//   //进程名字最大只保留128字符
//    char* processName=_processName;
//    if(NULL==_processName || strlen(_processName)>PROC_NAME_LIMIT){
//        processName="";
//    }

    //产生文件名
    char outFileName[PROC_NAME_LIMIT+HmDateTimeSz+48];
    genFileName(outFileName, "MemAllocLog", processName, pNow);

    FILE *outFilePtr=NULL;
    outFilePtr = fopen(outFileName, "w");
    if(outFilePtr == NULL)  {
      return  ;
    }

    int k;
    MAItem* logKPtr;
    fprintf(outFilePtr,"threadId,func,cnt,memSize,memAddr,memAddrOld,callerAddr,timespecCurrent.tv_sec,timespecCurrent.tv_nsec\n");
    for(k=0; k<memAllocCurIdx; k++){
        logKPtr= gMARoot.logArr + k;
        fprintf(outFilePtr,"0x%ux,%c,%zu,%zu,%p,%p,%p,%ld,%ld\n",logKPtr->threadId,logKPtr->func,logKPtr->cnt,logKPtr->memSize,logKPtr->memAddr,logKPtr->memAddrOld,logKPtr->callerAddr,logKPtr->timespecCurrent.tv_sec,logKPtr->timespecCurrent.tv_nsec);

    }

    fclose(outFilePtr);
    return  ;
}
#endif //MEMALLOCLOG_H